﻿#include <QDebug>
#include "QlyAVCodec.h"
#include "QlyAVCodecContext.h"
#include <libavutil/opt.h>
static bool msg(const char * str, int ret)
{
    static char err[512];
    if(ret < 0)
    {
        av_strerror(ret, err, 512);
        qWarning() << str << " error:" << err;
        exit(ret);
    }
    else
    {
        qDebug() << str << " : success";
    }
    return true;
}

QlyAVCodecContext::QlyAVCodecContext()
{


}

int64_t QlyAVCodecContext::bitRate() const
{
    if(!pCodecCtx) return -1;
    return pCodecCtx->bit_rate;
}

void QlyAVCodecContext::setBitRate(int64_t bitRate)
{
    if(pCodecCtx)
    {
        pCodecCtx->bit_rate = bitRate;
    }
}

void QlyAVCodecContext::setGopSize(int size)
{
    if(pCodecCtx)
    {
        pCodecCtx->gop_size = size;
    }
}

void QlyAVCodecContext::setMaxBFrame(int n)
{
    if(pCodecCtx)
    {
        pCodecCtx->max_b_frames = n;
    }
}

void QlyAVCodecContext::setPixelFormat(AVPixelFormat format)
{
    if(pCodecCtx)
    {
        pCodecCtx->pix_fmt = format;
    }
}

void QlyAVCodecContext::setTimeBase(AVRational timebase)
{
    if(pCodecCtx)
    {
        pCodecCtx->time_base = timebase;
    }
}

AVRational QlyAVCodecContext::timeBase()
{
    if(pCodecCtx)
    {
        return pCodecCtx->time_base;
    }
    return AVRational({1, 25});
}

void QlyAVCodecContext::setFrameRate(AVRational rate)
{
    if(pCodecCtx)
    {
        pCodecCtx->framerate = rate;
    }
    //av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);
}

void QlyAVCodecContext::setFrameRate(double rate)
{
    if(pCodecCtx)
    {
        /// TODO 这里需要个算法。把浮点数转化成分数
    }
}

int QlyAVCodecContext::width() const
{
    if(!pCodecCtx) return -1;
    return pCodecCtx->width;
}

int QlyAVCodecContext::height() const
{
    if(!pCodecCtx) return -1;
    return pCodecCtx->height;
}

void QlyAVCodecContext::setWidth(int width)
{
    pCodecCtx->width = width;
}

void QlyAVCodecContext::setHeight(int height)
{
    pCodecCtx->height = height;
}

bool QlyAVCodecContext::encodeFrame(const AVFrame * const pFrame, QQueue<QlyAVPacket> &queue)
{
    errorcode = avcodec_send_frame(pCodecCtx, pFrame);
    if(errorcode < 0) return false;
    while (errorcode >= 0)
    {
        QlyAVPacket packet;
        errorcode = avcodec_receive_packet(pCodecCtx, packet.ptr());
        if (errorcode == AVERROR(EAGAIN) || errorcode == AVERROR_EOF)
            return true;
        else if (errorcode < 0)
        {
            return false;
        }
        queue.enqueue(packet);
    }
}

bool QlyAVCodecContext::decodePacket(const AVPacket *pkt, QQueue<QlyAVFrame> &queue)
{
    errorcode = avcodec_send_packet(pCodecCtx, pkt);
    if(errorcode < 0) return false;
    while (errorcode >= 0)
    {
        QlyAVFrame frame;
        errorcode = avcodec_receive_frame(pCodecCtx, frame.ptr());
        if(errorcode < 0)
        {
            if(errorcode == AVERROR_EOF || errorcode == AVERROR(EAGAIN))
            {
                return true;
            }
            return false;
        }
        queue.enqueue(frame);
    }
    return false;
}

int QlyAVCodecContext::sendFrame(QlyAVFrame frame)
{
    errorcode = avcodec_send_frame(pCodecCtx, frame.ptr());
    if (errorcode < 0)
    {
        qWarning() << "Error sending a frame for encoding\n";
    }
    return errorcode;
}

int QlyAVCodecContext::opt_set(const char *name, const char *val, int search_flags)
{
    return av_opt_set(pCodecCtx, name, val, search_flags);
}

bool QlyAVCodecContext::openCodec(QlyAVCodec *pCodec, const AVCodecParameters *codecpar)
{
    if(pCodecCtx == nullptr)
    {
        pCodecCtx = avcodec_alloc_context3(pCodec->ptr());
    }
    if(pCodecCtx == nullptr)
    {
        qWarning() << "Cannot alloc pCodecCtx";
        return false;
    }
    if(codecpar)
    {
        errorcode = avcodec_parameters_to_context(pCodecCtx, codecpar);
        msg("openCodec::avcodec_parameters_to_context", errorcode);
    }
    errorcode = avcodec_open2(pCodecCtx, pCodec->ptr(), nullptr) ;
    return msg("openCodec::avcodec_open2", errorcode);
}

bool QlyAVCodecContext::allocContext(QlyAVCodec *pCodec)
{
    if(!pCodecCtx)
    {
        pCodecCtx = avcodec_alloc_context3(pCodec->ptr());
    }
    else
    {
        avcodec_free_context(&pCodecCtx);
        pCodecCtx = avcodec_alloc_context3(pCodec->ptr());
    }
    if(pCodecCtx)
    {
        pCodecCtx->codec_id = pCodec->id();
    }
    return pCodecCtx;
}

bool QlyAVCodecContext::openCodec(AVCodec *pCodec, const AVCodecParameters *codecpar)
{
    pCodecCtx = avcodec_alloc_context3(pCodec);
    if(pCodecCtx == nullptr)
    {
        qWarning() << "Cannot alloc pCodecCtx";
        return false;
    }
    if(codecpar)
    {
        errorcode = avcodec_parameters_to_context(pCodecCtx, codecpar);
        msg("openCodec::avcodec_parameters_to_context", errorcode);
    }
    errorcode = avcodec_open2(pCodecCtx, pCodec, nullptr) ;
    return msg("openCodec::avcodec_open2", errorcode);
}

QlyAVCodecContext::~QlyAVCodecContext()
{
    avcodec_free_context(&pCodecCtx);
}
